#include <bits/stdc++.h>
#pragma GCC optimize ("O2,unroll-loops")
#pragma GCC optimize("no-stack-protector,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")

using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef pair<ll, ll> pll;
#define debug(x) cerr<<#x<<'='<<(x)<<endl;
#define debugp(x) cerr<<#x<<"= {"<<(x.first)<<", "<<(x.second)<<"}"<<endl;
#define debug2(x, y) cerr<<"{"<<#x<<", "<<#y<<"} = {"<<(x)<<", "<<(y)<<"}"<<endl;
#define debugv(v) {cerr<<#v<<" : ";for (auto x:v) cerr<<x<<' ';cerr<<endl;}
#define all(x) x.begin(), x.end()
#define pb push_back
#define kill(x) return cout<<x<<'\n', 0;

const int inf=1000000010;
const ll INF=10000000000000010LL;
const int mod=1000000007;
const int MAXN=200010, LOG=20;

ll n, m, k, u, v, x, y, t, a, b, ans;
ll A[MAXN];
// 线段树左闭右开, 不重不漏
ll seg[MAXN<<2], lazy[MAXN<<2]; // 区间和, lazy标记位
int Mn[MAXN<<2], Mx[MAXN<<2];   // 区间最小值. 最大值

void Build(int id, int tl, int tr){
	if (tr-tl==1){                   // 终于递归走到叶子节点
		Mn[id]=Mx[id]=seg[id]=A[tl];   // 左开右闭
		return ;
	}
	int mid=(tl+tr)>>1;
	Build(id<<1, tl, mid);           // 建左子树
	Build(id<<1 | 1, mid, tr);       // 建右子树
	Mn[id]=min(Mn[id<<1], Mn[id<<1 | 1]); // 取左子树最大值
	Mx[id]=max(Mx[id<<1], Mx[id<<1 | 1]); // 取右子树最大值
	seg[id]=seg[id<<1] + seg[id<<1 | 1];	// 求和
}
// 添加lazy标记, 把id以下长度为len的节点都加上一个val
inline void add_lazy(int id, int len, ll val){
	Mn[id]=val;
	Mx[id]=val;
	lazy[id]=val;
	seg[id]=len*val;  // 因为整个区间都变成val了, 所以区间和就是len*val没错
}
// 下放lazy标记z
inline void shift(int id, int tl, int tr){
	if (!lazy[id]) return ;
	int mid=(tl+tr)>>1;
	add_lazy(id<<1, mid-tl, lazy[id]);    // 左子树加上mid-tl个val
	add_lazy(id<<1 | 1, tr-mid, lazy[id]);// 右子树
	lazy[id]=0;                           // 清lazy
}
// 把pos之前比val小的全部变成val
void Maximize(int id, int tl, int tr, int pos, ll val){
	if (pos<=tl || val<=Mn[id]) return ; // pos越过左边界
	if (tr<=pos && Mx[id]<=val){         // pos越过右边界
		add_lazy(id, tr-tl, val);          // id之下加个lazy
		return ;
	}
  // 下方标记并更新左右子树
	shift(id, tl, tr);
	int mid=(tl+tr)>>1;
	Maximize(id<<1, tl, mid, pos, val);
	Maximize(id<<1 | 1, mid, tr, pos, val);
  // 更新三个记录
	Mn[id]=min(Mn[id<<1], Mn[id<<1 | 1]);
	Mx[id]=max(Mx[id<<1], Mx[id<<1 | 1]);
	seg[id]=seg[id<<1] + seg[id<<1 | 1];
}
// Binary Search 第一轮能不能吃掉这一段
int BS1(int id, int tl, int tr, int pos, ll val){
	if (tr<=pos || val<Mn[id]) return tr;  // pos越过右边界或者钱太少, 返回右边界
	if (tr-tl==1) return tl;               // 没有节点了, 返回左边界
	shift(id, tl, tr);                     // 下放标记
	int mid=(tl+tr)>>1, tmp=BS1(id<<1, tl, mid, pos, val);  // 左子树
	if (tmp==mid) return BS1(id<<1 | 1, mid, tr, pos, val); // 右子树
	return tmp;
}
// Binary Search 第二轮找下一家吃什么
int BS2(int id, int tl, int tr, ll val){
	if (seg[id]<=val) return tr;   // 如果总和比val小: 就吃它了
	if (tr-tl==1) return tl;       // 递归到底了
	shift(id, tl, tr);             // 下放lazy标记
	int mid=(tl+tr)>>1, tmp=BS2(id<<1, tl, mid, val); // 在左子树中找
	if (tmp<mid) return tmp;
	return BS2(id<<1 | 1, mid, tr, val-seg[id<<1]);   // 吃掉左子树下所有餐馆, 右子树中找
}
//
ll Get(int id, int tl, int tr, int l, int r){
	if (r<=tl || tr<=l) return 0;  // 越界
	if (l<=tl && tr<=r) return seg[id]; // 在界限之内, 符合线段树的要求
	shift(id, tl, tr);               // 下放
	int mid=(tl+tr)>>1;
  // 在左右子树中寻找
	return Get(id<<1, tl, mid, l, r) + Get(id<<1 | 1, mid, tr, l, r);
}

int main(){
	ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	//freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
	cin>>n>>m;
	for (int i=1; i<=n; i++) cin>>A[i];
	Build(1, 1, n+1);
	while (m--){
		cin>>t>>x>>y;
		if (t==1) Maximize(1, 1, n+1, x+1, y);
		else{
			ans=0;
			while (1){
				x=BS1(1, 1, n+1, x, y);        //
				if (x==n+1) break ;            // 穷, 吃不起
				ll val=y+Get(1, 1, n+1, 1, x); // 因为是求到的是前缀和, 所以先加上
				int xx=BS2(1, 1, n+1, val);    // 这是从头开始吃能吃掉的饭馆数
				// buy [x, xx)
				ans+=xx-x;
				y-=Get(1, 1, n+1, x, xx);      // 再减去, 吃掉[x, xx)
				x=xx;                          // 前进! 继续从xx开吃
			}
			cout<<ans<<"\n";
		}
	}

	return 0;
}
